﻿//-----------------------------------------------------------------------
// <copyright file="QueryableExtensions.cs" company="SLWY Enterprises">
// * Copyright (C) 2017 四川商旅无忧科技有限公司 版权所有。
// * version : 1.0
// * author  : qinchaoyue
// * FileName: QueryableExtensions.cs
// * history : created by qinchaoyue 2018-01-12 02:59:53
// </copyright>
//-----------------------------------------------------------------------

using System;
using System.Collections.Generic;
using System.Linq;
using Slwy.Data.Entity.Attributes;
using Slwy.Data.Entity.Helper;
using SqlSugar;
using Slwy.Data.Entity.Models;
using Slwy.Utility.Extensions;

namespace Slwy.Data.Entity.SqlSugarExtensions
{
    public static class QueryableExtensions
    {
        public static ISugarQueryable<T> Table<T>(this ISugarQueryable<T> superset, string tableName)
        {
            return superset.AS(tableName);
        }

        /// <summary>
        /// 获取月表名
        /// </summary>
        /// <typeparam name="T">Entity实体</typeparam>
        /// <param name="superset">更新器</param>
        /// <param name="keyId">keyId</param>
        /// <exception cref="System.ArgumentException">>如果从keyId获取表名失败引发异常</exception>
        /// <returns></returns>
        public static ISugarQueryable<T> TableYYYY<T>(this ISugarQueryable<T> superset, string keyId)
        {
            return superset.AS(TableHelper.GetTableName<T>(keyId));
        }

        /// <summary>
        /// 获取月表名
        /// </summary>
        /// <typeparam name="T">Entity实体</typeparam>
        /// <param name="superset">更新器</param>
        /// <param name="keyId">keyId</param>
        /// <exception cref="System.ArgumentException">>如果从keyId获取表名失败引发异常</exception>
        /// <returns></returns>
        public static ISugarQueryable<T> TableYYYYMM<T>(this ISugarQueryable<T> superset, string keyId)
        {
            return superset.AS(TableHelper.GetTableName<T>(keyId, "YYMM"));
        }

        /// <summary>
        /// 获取分库表数据
        /// </summary>
        /// <typeparam name="TEntity"></typeparam>
        /// <param name="superset"></param>
        /// <returns></returns>
        public static List<TEntity> ToPartList<TEntity>(this ISugarQueryable<TEntity> superset)
            where TEntity : class, new()
        {
            return superset.ToPartPageList(1, 100000, out var count, true);
        }

        /// <summary>
        /// 分页获取数据
        /// </summary>
        /// <param name="superset"></param>
        /// <param name="pageIndex"></param>
        /// <param name="pageSize"></param>
        /// <param name="totalNumber"></param>
        /// <returns></returns>
        public static List<TEntity> ToPartPageList<TEntity>(this ISugarQueryable<TEntity> superset, int pageIndex, int pageSize, out int totalNumber, bool isCache)
            where TEntity : class, new()
        {
            var tableParts = GetTablePart<TEntity>(superset, GetSqlHash(superset), isCache);
            totalNumber = tableParts.Sum(m => m.DataCount);
            var entitys = new List<TEntity>();
            var tablePart = tableParts.Pop();
            var tmpCount = tablePart.DataCount;
            var skipCount = (pageIndex - 1) * pageSize;
            var take = pageSize;
            do
            {
                if (tmpCount >= (pageIndex -1) * pageSize)
                {
                    var partEntitys = superset.Table(tablePart.TableName).Skip(skipCount).Take(take).ToList();
                    take = take - partEntitys.Count();
                    entitys.AddRange(partEntitys);
                }
                skipCount = skipCount - tablePart.DataCount;

                if (tableParts.Count == 0)
                {
                    return entitys;
                }
                tablePart = tableParts.Pop();
                tmpCount += tablePart.DataCount;

            } while (take != 0);

            return entitys;
        }

        private static string GetSqlHash<TEntity>(ISugarQueryable<TEntity> superset)
        {
            var sql = superset.QueryBuilder.ToSqlString();
            foreach (var queryBuilderParameter in superset.QueryBuilder.Parameters)
            {
                sql = sql.Replace(queryBuilderParameter.ParameterName, queryBuilderParameter.Value.ToString());
            }

            return sql.GetMD5();
        }

        /// <summary>
        /// 获取分表的表中符合条件的数据条数
        /// 并缓存
        /// </summary>
        /// <typeparam name="TEntity">数据类型</typeparam>
        /// <param name="superset">SugarQueryable查询器</param>
        /// <param name="sqlHash">sql语句hash, 用于缓存数据</param>
        /// <param name="isCache">是否缓存 符合条件的数据条数</param>
        /// <returns></returns>
        private static Stack<MTablePart> GetTablePart<TEntity>(ISugarQueryable<TEntity> superset, string sqlHash, bool isCache)
            where TEntity : class, new()
        {
            var typeInfo = typeof(TEntity);
            var tableInfo = typeInfo.GetAttribute<TableInfoAttribute>();
            var tableFormat = tableInfo.PartBeginTime.Split("-").Length > 1 ? "YYMM" : "YY";
            var tableName = TableHelper.GetTableName<TEntity>(DateTime.Now.ToString("yyMM"), tableFormat);
            if (BaseRepository<TEntity>._tableParts.TryGetValue(sqlHash, out var tableParts) || isCache)
            {
                if (tableParts?.First().UpDateTime.AddMinutes(10) >= DateTime.Now)
                {
                    var newTableParts = tableParts.Clone();
                    newTableParts.Push(new MTablePart
                    {
                        TableName = tableName,
                        DataCount = superset.Table(tableName).Count(),
                        UpDateTime = DateTime.Now,
                    });
                    return newTableParts;
                }
            }

            if (!typeInfo.HasAttribute<TableInfoAttribute>())
            {
                if (!tableInfo.IsPart)
                {
                    BaseRepository<TEntity>._tableParts[sqlHash].Push(new MTablePart
                    {
                        TableName = TableHelper.GetTableName<TEntity>(),
                        DataCount = superset.Count(),
                        UpDateTime = DateTime.Now,
                    });
                    return BaseRepository<TEntity>._tableParts[sqlHash].Clone();
                }
            }

            var tableTime = Convert.ToDateTime(tableInfo.PartBeginTime);
            BaseRepository<TEntity>._tableParts.Add(sqlHash, new Stack<MTablePart>());
            do
            {
                tableName = TableHelper.GetTableName<TEntity>(tableTime.ToString("yyMM"), tableFormat);

                BaseRepository<TEntity>._tableParts[sqlHash].Push(
                    new MTablePart
                    {
                        TableName = tableName,
                        DataCount = superset.Table(tableName).Count(),
                        UpDateTime = DateTime.Now,
                    });
                if (tableFormat == "YY")
                    tableTime = tableTime.AddYears(1);
                else
                    tableTime = tableTime.AddMonths(1);
            } while (tableTime.ToString("yyyyMM") != DateTime.Now.ToString("yyyyMM"));

            var newTablePart = BaseRepository<TEntity>._tableParts[sqlHash].Clone();
            tableName = TableHelper.GetTableName<TEntity>(DateTime.Now.ToString("yyMM"), tableFormat);
            newTablePart.Push(new MTablePart
                {
                    TableName = tableName,
                    DataCount = superset.Table(tableName).Count(),
                    UpDateTime = DateTime.Now,
                });
            return newTablePart;
        }
    }
}
